/*
 * Copyright (c) 2017-present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.core.filters;

import com.cv4j.core.datamodel.ColorProcessor;
import com.cv4j.core.datamodel.ImageProcessor;
import com.cv4j.image.util.Tools;

import java.util.Optional;

/**
 * 自然过滤
 *
 * @author dev
 * @date 2021/07/22
 */
public class NatureFilter extends BaseFilter {
    /**
     * 大气的风格
     */
    public static final int ATMOSPHERE_STYLE = 1;
    /**
     * 燃烧的风格
     */
    public static final int BURN_STYLE = 2;
    /**
     * 雾的风格
     */
    public static final int FOG_STYLE = 3;
    /**
     * 冻结的风格
     */
    public static final int FREEZE_STYLE = 4;
    /**
     * 熔岩风格
     */
    public static final int LAVA_STYLE = 5;
    /**
     * 金属风格
     */
    public static final int METAL_STYLE = 6;
    /**
     * 海洋风格
     */
    public static final int OCEAN_STYLE = 7;
    /**
     * 水的风格
     */
    public static final int WATER_STYLE = 8;
    /**
     * 风格
     */
    private int style;
    /**
     * 雾查找
     */
    private int[] fogLookUp;

    /**
     * 自然过滤
     */
    public NatureFilter() {
        this(ATMOSPHERE_STYLE);
    }

    /**
     * 自然过滤
     *
     * @param style 风格
     */
    public NatureFilter(int style) {
        this.style = style;
        buildFogLookupTable();
    }

    /**
     * 建立雾查找表
     */
    private void buildFogLookupTable() {
        fogLookUp = new int[256];
        int fogLimit = 40;
        for (int i = 0; i < fogLookUp.length; i++) {
            if (i > 127) {
                fogLookUp[i] = i - fogLimit;
                if (fogLookUp[i] < 127) {
                    fogLookUp[i] = 127;
                }
            } else {
                fogLookUp[i] = i + fogLimit;
                if (fogLookUp[i] > 127) {
                    fogLookUp[i] = 127;
                }
            }
        }
    }

    /**
     * 得到的风格
     *
     * @return int
     */
    public int getStyle() {
        return style;
    }

    /**
     * 设置风格
     *
     * @param style 风格
     */
    public void setStyle(int style) {
        this.style = style;
    }

    /**
     * 做的过滤器
     *
     * @param src src
     * @return {@link ImageProcessor}
     */
    @Override
    public Optional<ImageProcessor> doFilter(ImageProcessor src) {
        int total = width * height;
        int ta = 0;
        int tr = 0;
        int tg = 0;
        int tb = 0;
        for (int i = 0; i < total; i++) {
            tr = RED[i] & 0xff;
            tg = GREED[i] & 0xff;
            tb = BLUE[i] & 0xff;
            int[] onePixel = processOnePixel(ta, tr, tg, tb);

            RED[i] = (byte) onePixel[0];
            GREED[i] = (byte) onePixel[1];
            BLUE[i] = (byte) onePixel[2];
        }
        ((ColorProcessor) src).putRGB(RED, GREED, BLUE);
        return Optional.ofNullable(src);
    }

    /**
     * 处理一个像素
     *
     * @param ta 助教
     * @param tr tr
     * @param tg tg
     * @param tb 结核病
     * @return {@link int[]}
     */
    private int[] processOnePixel(int ta, int tr, int tg, int tb) {
        int[] pixel = new int[4];
        pixel[0] = ta;
        int gray = (tr + tg + tb) / 3;

        switch (style) {
            case BURN_STYLE:
                pixel[1] = Tools.clamp(gray * 3);
                pixel[2] = gray;
                pixel[3] = gray / 3;
                break;
            case FOG_STYLE:
                pixel[1] = fogLookUp[tr];
                pixel[2] = fogLookUp[tg];
                pixel[3] = fogLookUp[tb];
                break;
            case FREEZE_STYLE:
                pixel[1] = Tools.clamp((int) Math.abs((tr - tg - tb) * 1.5));
                pixel[2] = Tools.clamp((int) Math.abs((tg - tb - pixel[1]) * 1.5));
                pixel[3] = Tools.clamp((int) Math.abs((tb - pixel[1] - pixel[2]) * 1.5));
                break;
            case LAVA_STYLE:
                pixel[1] = gray;
                pixel[2] = Math.abs(tb - 128);
                pixel[3] = Math.abs(tb - 128);
                break;
            case METAL_STYLE:
                int[] tPixel = metalStyle(ta,tr);
                pixel[1] = tPixel[1];
                pixel[2] = tPixel[2];
                pixel[3] = tPixel[3];
                break;
            case OCEAN_STYLE:
                pixel[1] = Tools.clamp(gray / 3);
                pixel[2] = gray;
                pixel[3] = Tools.clamp(gray * 3);
                break;
            case WATER_STYLE:
                pixel[1] = Tools.clamp(gray - tg - tb);
                pixel[2] = Tools.clamp(gray - pixel[1] - tb);
                pixel[3] = Tools.clamp(gray - pixel[1] - pixel[2]);
                break;
            default:
                pixel[1] = (tg + tb) / 2;
                pixel[2] = (tr + tb) / 2;
                pixel[3] = (tg + tr) / 2;
                break;
        }
        return pixel;
    }

    private int[] metalStyle(int ta, int tr) {
        int[] pixel = new int[4];
        pixel[0] = ta;
        float red = Math.abs(tr - 64);
        float greed = Math.abs(red - 64);
        float blue = Math.abs(greed - 64);
        float grayFloat = ((222 * red + 707 * greed + 71 * blue) / 1000);
        red = grayFloat + 70;
        red = red + (((red - 128) * 100) / 100f);
        greed = grayFloat + 65;
        greed = greed + (((greed - 128) * 100) / 100f);
        blue = grayFloat + 75;
        blue = blue + (((blue - 128) * 100) / 100f);
        pixel[1] = Tools.clamp((int) red);
        pixel[2] = Tools.clamp((int) greed);
        pixel[3] = Tools.clamp((int) blue);
        return pixel;
    }
}
